package com.fedex;

import com.fedex.locations.stub.*;
import org.apache.axis.types.NonNegativeInteger;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Calendar;

/**
 * Sample code to call the FedEx Locator service with Axis
 * <p/>
 * com.fedex.dropoff.stub is generated via WSDL2Java, like this:<br>
 * <pre>
 * java org.apache.axis.wsdl.WSDL2Java -w -p com.fedex.location.stub http://www.fedex.com/...../LocactionsService?wsdl
 * </pre>
 * <p/>
 * This sample code has been tested with JDK 5 and Apache Axis 1.4
 */
public class LocationsSearchWebServiceClient {
    //
    public static void main(String[] args) {
        //
        // Build decode DropoffLocatorRequest object
        SearchLocationsRequest request = new SearchLocationsRequest();

        request.setClientDetail(createClientDetail());
        request.setWebAuthenticationDetail(createWebAuthenticationDetail());

        //
        TransactionDetail transactionDetail = new TransactionDetail();
        transactionDetail.setCustomerTransactionId("java sample - Locations Search"); //This is decode reference field for the customer.  Any value can be used and will be provided in the return.
        request.setTransactionDetail(transactionDetail);

        //
        VersionId versionId = new VersionId("locs", 1, 0, 0);
        request.setVersion(versionId);
        //
        request.setPhoneNumber("1234567890");
        Address nearToAddress = new Address();
        nearToAddress.setStreetLines(new String[]{"10 FedEx Pkwy"});
        nearToAddress.setCity("Collierville");
        nearToAddress.setStateOrProvinceCode("TN");
        nearToAddress.setPostalCode("38017");
        nearToAddress.setCountryCode("US");
        request.setAddress(nearToAddress);
        //
        request.setEffectiveDate(Calendar.getInstance().getTime());
        request.setLocationsSearchCriterion(LocationsSearchCriteriaType.ADDRESS);
        request.setMultipleMatchesAction(MultipleMatchesActionType.RETURN_ALL);
        LocationSortDetail locationSortDetail = new LocationSortDetail();
        locationSortDetail.setCriterion(LocationSortCriteriaType.DISTANCE);
        locationSortDetail.setOrder(LocationSortOrderType.LOWEST_TO_HIGHEST);
        request.setSortDetail(locationSortDetail);
        SearchLocationConstraints searchLocationConstraints = new SearchLocationConstraints();
        Distance radiusDistance = new Distance();
        radiusDistance.setUnits(DistanceUnits.MI);
        radiusDistance.setValue(new BigDecimal(10));
        searchLocationConstraints.setRadiusDistance(radiusDistance);
        searchLocationConstraints.setResultsFilters(new LocationSearchFilterType[]{LocationSearchFilterType.EXCLUDE_LOCATIONS_OUTSIDE_STATE_OR_PROVINCE});
        searchLocationConstraints.setSupportedRedirectToHoldServices(new SupportedRedirectToHoldServiceType[]{SupportedRedirectToHoldServiceType.FEDEX_EXPRESS});
        searchLocationConstraints.setRequiredLocationAttributes(new LocationAttributesType[]{LocationAttributesType.ACCEPTS_CASH});
        searchLocationConstraints.setResultsToSkip(new NonNegativeInteger("1"));
        searchLocationConstraints.setLocationContentOptions(new LocationContentOptionType[]{LocationContentOptionType.HOLIDAYS});
        searchLocationConstraints.setLocationTypesToInclude(new FedExLocationType[]{FedExLocationType.FEDEX_OFFICE});
        request.setConstraints(searchLocationConstraints);
        //
        try {
            // Initialize the service
            LocationsServiceLocator service;
            LocationsPortType port;
            //
            service = new LocationsServiceLocator();
            updateEndPoint(service);
            port = service.getLocationsServicePort();
            // This is the call to the web service passing in decode SearchLocationsRequest and returning decode SearchLocationsReply
            SearchLocationsReply reply = port.searchLocations(request);
            //
            if (isResponseOk(reply.getHighestSeverity())) {
                printString(reply.getTotalResultsAvailable().toString(), "Total Results Available");
                printString(reply.getResultsReturned().toString(), "Results Returned");
                AddressToLocationRelationshipDetail[] addressToLocationRelationship = reply.getAddressToLocationRelationships();
                for (int i = 0; i < addressToLocationRelationship.length; i++) {
                    printMatchedAddress(addressToLocationRelationship[i].getMatchedAddress());
                    System.out.println();
                    System.out.println();
                    printDistanceAndLocationDetail(addressToLocationRelationship[i].getDistanceAndLocationDetails());
                }

            }

            printNotifications(reply.getNotifications());

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private static void printMatchedAddress(Address matchAddress) {
        if (matchAddress != null) {
            System.out.println("Matched Address");
            String streetLine[] = matchAddress.getStreetLines();
            if (streetLine != null) {
                for (int j = 0; j < streetLine.length; j++) {
                    printString("", streetLine[j], " ");
                }
            }
            printString(matchAddress.getCity(), "City", " ");
            printString(matchAddress.getStateOrProvinceCode(), "State/Province", " ");
            printString(matchAddress.getPostalCode(), "Postal Code", " ");
            printString(matchAddress.getCountryCode(), "Country", " ");
        }
    }

    private static void printLocationContactAndAddress(LocationContactAndAddress locationContactAndAddress) {
        if (locationContactAndAddress != null) {
            Contact locationContact = locationContactAndAddress.getContact();
            if (locationContact != null) {
                System.out.println("Location Contact");
                printString(locationContact.getCompanyName(), "Company", "  ");
                printString(locationContact.getPersonName(), "Contact", "  ");
                printString(locationContact.getPhoneNumber(), "Phone Number", "  ");
                printString(locationContact.getPhoneExtension(), "Phone Ext", "  ");
            }
            Address locationAddress = locationContactAndAddress.getAddress();
            if (locationAddress != null) {
                System.out.println("Location Address");
                String[] streetLine = locationAddress.getStreetLines();
                if (streetLine != null) {
                    for (int i = 0; i < streetLine.length; i++) {
                        printString(streetLine[i], "Streetline", "  ");
                    }
                }
                printString(locationAddress.getCity(), "City", "  ");
                printString(locationAddress.getStateOrProvinceCode(), "State or Province", "  ");
                printString(locationAddress.getPostalCode(), "Postal Code", "  ");
                printString(locationAddress.getCountryCode(), "Country Code", "  ");
            }
        }
    }

    private static void printLocationAttibutes(LocationAttributesType[] locationAttributesType) {
        if (locationAttributesType != null) {
            System.out.println("Location Attributes");
            for (int m = 0; m < locationAttributesType.length; m++) {
                printString(locationAttributesType[m].getValue(), "", " ");
            }
        }
    }

    private static void printLocationDetail(LocationDetail locationDetail) {
        if (locationDetail != null) {
            printString(locationDetail.getLocationId(), "Location Id", "");
            printString(locationDetail.getStoreNumber().toString(), "Store Number", " ");
            printString(locationDetail.getLocationType().getValue(), "Location Type", " ");
            printString(locationDetail.getGeographicCoordinates(), "Geographic Coordinates", " ");
            printLocationContactAndAddress(locationDetail.getLocationContactAndAddress());
            printLocationAttibutes(locationDetail.getAttributes());
            printCarrierDetails(locationDetail.getCarrierDetails());
            if (!locationDetail.getLocationType().equals(FedExLocationType.FEDEX_SELF_SERVICE_LOCATION)) {
                printLocationHours(locationDetail.getNormalHours(), "Location Normal Hours");
                printLocationHours(locationDetail.getHoursForEffectiveDate(), "Hours based on the Effective Date");
                printLocationHours(locationDetail.getExceptionalHours(), "Location Exceptional Hours");
            }
        }
    }

    private static void printLocationHours(LocationHours[] locationHours, String description) {
        if (locationHours != null) {
            System.out.println(description);
            for (int i = 0; i < locationHours.length; i++) {
                if (locationHours[i].getOperationalHours().equals(OperationalHoursType.OPEN_BY_HOURS)) {
                    printTimeRange(locationHours[i].getDayofWeek().toString(),
                            locationHours[i].getHours());
                } else {
                    printString(locationHours[i].getOperationalHours().toString(), locationHours[i].getDayofWeek().toString(), " ");
                }
            }
        }
    }

    private static void printTimeRange(String day, TimeRange[] timeRange) {
        if (timeRange != null) {
            for (int i = 0; i < timeRange.length; i++) {
                String open = timeRange[i].getBegins().toString();
                String close = timeRange[i].getEnds().toString();
                System.out.println(" " + day + ": " + open + "-" + close);
            }
        }
    }

    private static void printCarrierDetails(CarrierDetail[] carrierDetail) {
        if (carrierDetail != null) {
            for (int i = 0; i < carrierDetail.length; i++) {
                printString(carrierDetail[i].getCarrier().toString(), "Carrier Detail", "");
                printLatestDropOffDetails(carrierDetail[i].getNormalLatestDropOffDetails(), "Normal Last DropOff");
                System.out.println("Last DropOff based on Effective Date");
                printLatestDropOffDetail(carrierDetail[i].getEffectiveLatestDropOffDetails());
                printLatestDropOffDetails(carrierDetail[i].getExceptionalLatestDropOffDetails(), "Last Dropoff Exceptional");
                printShippingHolidays(carrierDetail[i].getShippingHolidays());
                System.out.println();
            }
        }
    }

    private static void printShippingHolidays(ShippingHoliday[] shippingHoliday) {
        if (shippingHoliday != null) {
            System.out.println("Shipping Holidays");
            for (int i = 0; i < shippingHoliday.length; i++) {
                printString(shippingHoliday[i].getHoliday().getName(), "Holiday Name", " ");
                printString(shippingHoliday[i].getHoliday().getDate().toString(), "Date", " ");
            }
        }
    }

    private static void printLatestDropOffDetails(LatestDropOffDetail[] latestDropOffDetails, String description) {
        if (latestDropOffDetails != null) {
            System.out.println(description);
            for (int i = 0; i < latestDropOffDetails.length; i++) {
                printLatestDropOffDetail(latestDropOffDetails[i]);
            }
        }
    }

    private static void printLatestDropOffDetail(LatestDropOffDetail latestDropOffDetail) {
        printString(latestDropOffDetail.getTime().toString(), latestDropOffDetail.getDayOfWeek().toString(), " ");
    }

    private static void printDistanceAndLocationDetail(DistanceAndLocationDetail[] distanceAndLocationDetail) {
        if (distanceAndLocationDetail != null) {
            for (int i = 0; i < distanceAndLocationDetail.length; i++) {
                printDistance(distanceAndLocationDetail[i].getDistance(), "Distance", "");
                printLocationDetail(distanceAndLocationDetail[i].getLocationDetail());
                System.out.println();
            }
        }
    }

    private static boolean isResponseOk(NotificationSeverityType notificationSeverityType) {
        if (notificationSeverityType == null) {
            return false;
        }
        if (notificationSeverityType.equals(NotificationSeverityType.WARNING) ||
                notificationSeverityType.equals(NotificationSeverityType.NOTE) ||
                notificationSeverityType.equals(NotificationSeverityType.SUCCESS)) {
            return true;
        }
        return false;
    }


    private static ClientDetail createClientDetail() {
        ClientDetail clientDetail = new ClientDetail();
        String accountNumber = System.getProperty("accountNumber");
        String meterNumber = System.getProperty("meterNumber");

        //
        // See if the accountNumber and meterNumber properties are set,
        // if set use those values, otherwise default them to "XXX"
        //
        if (accountNumber == null) {
            accountNumber = "XXX"; // Replace "XXX" with clients account number
        }
        if (meterNumber == null) {
            meterNumber = "XXX"; // Replace "XXX" with clients meter number
        }
        clientDetail.setAccountNumber(accountNumber);
        clientDetail.setMeterNumber(meterNumber);
        return clientDetail;
    }

    private static WebAuthenticationDetail createWebAuthenticationDetail() {
        WebAuthenticationCredential wac = new WebAuthenticationCredential();
        String key = System.getProperty("key");
        String password = System.getProperty("password");

        //
        // See if the key and password properties are set,
        // if set use those values, otherwise default them to "XXX"
        //
        if (key == null) {
            key = "XXX"; // Replace "XXX" with clients key
        }
        if (password == null) {
            password = "XXX"; // Replace "XXX" with clients password
        }
        wac.setKey(key);
        wac.setPassword(password);
        return new WebAuthenticationDetail(wac);
    }

    private static void printNotifications(Notification[] notifications) {
        System.out.println("*************************************************************");
        System.out.println("Notifications:");
        if (notifications == null || notifications.length == 0) {
            System.out.println("  No notifications returned");
        }
        for (int i = 0; i < notifications.length; i++) {
            Notification n = notifications[i];
            System.out.print("  Notification no. " + i + ": ");
            if (n == null) {
                System.out.println("null");
                continue;
            } else {
                System.out.println("");
            }
            NotificationSeverityType nst = n.getSeverity();

            System.out.println("    Severity: " + (nst == null ? "null" : nst.getValue()));
            System.out.println("    Code: " + n.getCode());
            System.out.println("    Message: " + n.getMessage());
            System.out.println("    Source: " + n.getSource());
        }
    }

    private static void printString(String value, String description, String space) {
        if (value != null) {
            System.out.println(space + description + ": " + value);
        }
    }

    private static void printString(String value, String description) {
        printString(value, description, "");
    }

    private static void printDistance(Distance distance, String description, String space) {
        if (distance != null) {
            DecimalFormat decimalFormat = new DecimalFormat("#.##");
            String units = distance.getUnits().toString();
            String value = decimalFormat.format(distance.getValue().doubleValue());
            distance.getValue().doubleValue();
            System.out.println(space + description + ": " + value + " " + units);
        }
    }

    private static void updateEndPoint(LocationsServiceLocator serviceLocator) {
        String endPoint = System.getProperty("endPoint");
        if (endPoint != null) {
            serviceLocator.setLocationsServicePortEndpointAddress(endPoint);
        }
    }

}
